#ifndef TWITSERVER_H_
#define TWITSERVER_H_

#include <string>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <map>
#include <list>
#include <vector>
#include <sstream>
#include <fstream>

#include "Protocol.h"
#include "Message.h"

#define SUCCESS 0
#define FAILURE -1


//A struct representing the client from the server point of view:
typedef struct client
{
	//client socket FD:
	int _socketFd;
	//client name:
	string _name;
	//list of the socket FD's following the client:
	list<int> _followers;

}client;


using namespace std;

class TwitServer{

	//DATA MEMBERS

private:
	//consts:
	static const unsigned int MAX_NUM_OF_CONNECTIONS = 65535;

	//The log file name:
	static const string LOG_FILE_NAME;

	//Consts for client defined requests
	static const string LOG_CONNECT_MSG;
	static const string LOG_DISCONNECT_MSG;
	static const string LOG_FOLLOW_MSG;
	static const string LOG_UNFOLLOW_MSG;
	static const string LOG_TWIT_MSG;
	static const string LOG_DM_MSG;
	static const string LOG_PA_MSG;
	static const string LOG_WHO_MSG;

	//A const for user defined commands:
	static const string USR_EXIT_CMD;

	//a set of currently active connections.
	fd_set _activeConnections;
	//a vector of clients:
	vector<client*> _clients;
	int _maxFD;
	//FD of the listening socket:
	int _listener;
	//Port number of the listening socket:
	short int _port;
	//The server's address struct:
	struct sockaddr_in* _myAddress;
	//a file stream, used to write to the log file:
	ofstream _logStreamer;



	//FUNCTIONS:

	//Conncets to the the client whose FD is clientFD.
	//If a system call fails or a client with the same name (case insensitivity) already exist, the function will return FAILURE,
	//otherwise it will return SUCCESS:
	int connectToClient(int clientFD);

	//Handles all supported requests from an existing client whose socket FD is clientFD:
	int handleClientRequest(int clientFD);

	//Handles the 'DISCONNECT' request from a client whose socket FD is client FD:
	int disconnectClient (int clientFD);

	//Handles the 'FOLLOW' request from a client whose socket FD is client FD.
	//If the name of the client to follow does not exists, an error message would be sent to the requesting client,
	//and FAILURE would be returned, otherwise SUCCESS would be returned:
	int setUserFollowing(int clientFD, string followedClientName);

	//Handles the 'UNFOLLOW' request from a client whose socket FD is client FD.
	//If the name of the client to follow does not exists, an error message would be sent to the requesting client,
	//and FAILURE would be returned, otherwise SUCCESS would be returned:
	int setUserUnfollowing(int clientFD, string userToRemove);

	//Handles the 'TWIT' request from a client whose socket FD is client FD.
	//The twit will be sent to the followers of the twitting client:
	int postTwit(int clientFD, string twit, string timeString);

	//Handles the 'DM' request from a client whose socket FD is client FD.
	//If the name of recipient of the direct message does not exist, an error message would be sent to the requesting client,
	//and FAILURE would be returned, otherwise SUCCESS would be returned:
	int sendDirectMessage(int sendingClientFD, string recipientName, string twit, string timeString);

	//Handles the 'PM' request from a client whose socket FD is client FD:
	int sendPublicMessage(int sendingClientFD, string twit, string timeString);

	//Handles the 'WHO' request from a client whose socket FD is client FD.
	//Sends a list of all the existing clients at the moment:
	int sendListOfAllClients(int clientFD);

	//Sends all of the clients a disconnection message and removes all the clients from the existing clients lists:
	void disconnectFromClients();

	//returns a client pointer to the client whose name is clientName:
	client* getClientByName(string clientName);

	//returns a client pointer to the client whose socket FD is clientFD:
	client* getClientByFD(int clientFD);

	//Checks whether a client with the given clientName already exists (case insensitive).
	//returns true if such a name already exists, false otherwise:
	bool doesClientNameExists(string clientName);

	//Writes the given string to the log file:
	void writeToLog(string dataToWrite);

public:
	//main function of the server, listens to new connections, existing connections,
	//and user STDIN input and acts accordingly:
	void listenToSockets();

	//constructor:
	TwitServer(short int port);

	//destructor:
	~TwitServer();
};



#endif /* TWITSERVER_H_ */
